package eval

import (
	"showen7/eval"
	"showen7/inter"
	"showen7/lex"
	"showen7/parse"
	"testing"
)

func TestBooleanPredicate(t *testing.T) {
	tests := []struct {
		input string
		want  string
	}{
		{"(boolean? #t)", "#t"},
		{"(boolean? #f)", "#t"},
		{"(boolean? 123)", "#f"},
	}

	env := inter.NewEnvironment()
	eval.RegisterBuiltinProcedures(env)

	for _, tt := range tests {
		lexer := lex.NewLexer(tt.input)
		tokens := lexer.ReadTokens()
		parser := parse.NewParser(tokens)
		expr, err := parser.ParseExpression()
		if err != nil {
			t.Fatalf("parser.ParseExpression() error = %v", err)
		}

		var obj inter.Object
		obj, err = eval.EvalExpression(expr, env)
		if err != nil {
			t.Fatalf("EvalExpression() error = %v", err)
		}
		if obj.Inspect() != tt.want {
			t.Errorf("EvalExpression() = %v, want %v", obj.Inspect(), tt.want)
		}
	}
}

func TestSymbolPredicate(t *testing.T) {
	tests := []struct {
		input string
		want  string
	}{
		{"(symbol? 'foo)", "#t"},
		{"(symbol? 'BAR)", "#t"},
		{"(symbol? 123)", "#f"},
		{"(symbol? #t)", "#f"},
		{"(symbol? \"hello\")", "#f"},
		{"(symbol? #\\a)", "#f"},
	}

	env := inter.NewEnvironment()
	eval.RegisterBuiltinProcedures(env)

	for _, tt := range tests {
		lexer := lex.NewLexer(tt.input)
		tokens := lexer.ReadTokens()
		parser := parse.NewParser(tokens)
		expr, err := parser.ParseExpression()
		if err != nil {
			t.Fatalf("parser.ParseExpression() error = %v", err)
		}

		obj, err := eval.EvalExpression(expr, env)
		if err != nil {
			t.Fatalf("EvalExpression() error = %v", err)
		}
		if obj.Inspect() != tt.want {
			t.Errorf("EvalExpression(%s) = %v, want %v", tt.input, obj.Inspect(), tt.want)
		}
	}
}

func TestNumberPredicates(t *testing.T) {
	tests := []struct {
		input string
		want  string
	}{
		// number? tests
		{"(number? 123)", "#t"},
		{"(number? 123.45)", "#t"},
		{"(number? 1/2)", "#t"},
		{"(number? 1+2i)", "#t"},
		{"(number? 'foo)", "#f"},
		{"(number? #t)", "#f"},

		// integer? tests
		{"(integer? 123)", "#t"},
		{"(integer? 123.0)", "#f"}, // In this implementation, reals are not integers
		{"(integer? 1/1)", "#t"},   // Rationals with denominator 1 might be considered integers
		{"(integer? 123.45)", "#f"},
		{"(integer? 'foo)", "#f"},

		// rational? tests
		{"(rational? 1/2)", "#t"},
		{"(rational? 123)", "#t"}, // Integers are also rationals
		{"(rational? 123.45)", "#f"},
		{"(rational? 'foo)", "#f"},

		// real? tests
		{"(real? 123.45)", "#t"},
		{"(real? 123)", "#t"},  // Integers are also reals
		{"(real? 1/2)", "#t"},  // Rationals are also reals
		{"(real? 1+2i)", "#f"}, // Complex numbers with non-zero imaginary part are not reals
		{"(real? 1+0i)", "#t"}, // Complex numbers with zero imaginary part ARE reals (according to R4RS)
		{"(real? 'foo)", "#f"},

		// complex? tests
		{"(complex? 1+2i)", "#t"},
		{"(complex? 123)", "#t"}, // All numbers are complex in this implementation
		{"(complex? 123.45)", "#t"},
		{"(complex? 1/2)", "#t"},
		{"(complex? 'foo)", "#f"},
	}

	env := inter.NewEnvironment()
	eval.RegisterBuiltinProcedures(env)

	for _, tt := range tests {
		lexer := lex.NewLexer(tt.input)
		tokens := lexer.ReadTokens()
		parser := parse.NewParser(tokens)
		expr, err := parser.ParseExpression()
		if err != nil {
			t.Fatalf("parser.ParseExpression() error = %v", err)
		}

		obj, err := eval.EvalExpression(expr, env)
		if err != nil {
			t.Fatalf("EvalExpression() error = %v", err)
		}
		if obj.Inspect() != tt.want {
			t.Errorf("EvalExpression(%s) = %v, want %v", tt.input, obj.Inspect(), tt.want)
		}
	}
}

func TestCharPredicate(t *testing.T) {
	tests := []struct {
		input string
		want  string
	}{
		{"(char? #\\a)", "#t"},
		{"(char? #\\space)", "#t"},
		{"(char? #\\newline)", "#t"},
		{"(char? 123)", "#f"},
		{"(char? 'foo)", "#f"},
		{"(char? #t)", "#f"},
		{"(char? \"a\")", "#f"},
	}

	env := inter.NewEnvironment()
	eval.RegisterBuiltinProcedures(env)

	for _, tt := range tests {
		lexer := lex.NewLexer(tt.input)
		tokens := lexer.ReadTokens()
		parser := parse.NewParser(tokens)
		expr, err := parser.ParseExpression()
		if err != nil {
			t.Fatalf("parser.ParseExpression() error = %v", err)
		}

		obj, err := eval.EvalExpression(expr, env)
		if err != nil {
			t.Fatalf("EvalExpression() error = %v", err)
		}
		if obj.Inspect() != tt.want {
			t.Errorf("EvalExpression(%s) = %v, want %v", tt.input, obj.Inspect(), tt.want)
		}
	}
}

func TestStringPredicate(t *testing.T) {
	tests := []struct {
		input string
		want  string
	}{
		{"(string? \"hello\")", "#t"},
		{"(string? \"\")", "#t"},
		{"(string? \"123\")", "#t"},
		{"(string? 123)", "#f"},
		{"(string? 'foo)", "#f"},
		{"(string? #t)", "#f"},
		{"(string? #\\a)", "#f"},
	}

	env := inter.NewEnvironment()
	eval.RegisterBuiltinProcedures(env)

	for _, tt := range tests {
		lexer := lex.NewLexer(tt.input)
		tokens := lexer.ReadTokens()
		parser := parse.NewParser(tokens)
		expr, err := parser.ParseExpression()
		if err != nil {
			t.Fatalf("parser.ParseExpression() error = %v", err)
		}

		obj, err := eval.EvalExpression(expr, env)
		if err != nil {
			t.Fatalf("EvalExpression() error = %v", err)
		}
		if obj.Inspect() != tt.want {
			t.Errorf("EvalExpression(%s) = %v, want %v", tt.input, obj.Inspect(), tt.want)
		}
	}
}

func TestProcedurePredicate(t *testing.T) {
	tests := []struct {
		input string
		want  string
	}{
		{"(procedure? char?)", "#t"},
		{"(procedure? (lambda (x) x))", "#t"},
		{"(procedure? #t)", "#f"},
		{"(procedure? #\\a)", "#f"},
	}

	env := inter.NewEnvironment()
	eval.RegisterBuiltinProcedures(env)

	for _, tt := range tests {
		lexer := lex.NewLexer(tt.input)
		tokens := lexer.ReadTokens()
		parser := parse.NewParser(tokens)
		expr, err := parser.ParseExpression()
		if err != nil {
			t.Fatalf("parser.ParseExpression() error = %v", err)
		}

		obj, err := eval.EvalExpression(expr, env)
		if err != nil {
			t.Fatalf("EvalExpression() error = %v", err)
		}
		if obj.Inspect() != tt.want {
			t.Errorf("EvalExpression(%s) = %v, want %v", tt.input, obj.Inspect(), tt.want)
		}
	}
}
